<?php


namespace app\services\wechat;

use app\common\model\Config;

use think\Exception;
use think\facade\Log;


class Offiaccount
{

    public function __construct()
    {
        $config = [
            'appid' => Config::getConfigValue('gzh_appid'),
            'appSecret' => Config::getConfigValue('gzh_appSecret'),
            'mchid' => Config::getConfigValue('gzh_mch_id'),
            'key_file' => Config::getConfigValue('gzh_key_file'),
            'cret_file' => Config::getConfigValue('gzh_cret_file'),
            'serial_number' => Config::getConfigValue('gzh_serial_number'),


            'm_appSecret' => Config::getConfigValue('gzh_m_appSecret'),
            'sp_appid' => Config::getConfigValue('gzh_sp_appid'),
            'sp_mchid' => Config::getConfigValue('gzh_sp_mchid'),
            //'sub_appid' => Config::getConfigValue('gzh_sub_appid'),
            'sub_mchid' => Config::getConfigValue('gzh_sub_mchid'),
            'm_key_file' => Config::getConfigValue('gzh_m_key_file'),
            'm_cret_file' => Config::getConfigValue('gzh_m_cret_file'),
            'm_serial_number' => Config::getConfigValue('gzh_m_serial_number'),
            'pay_type' => Config::getConfigValue('pay_type'),

        ];
        $this->config = $config;
    }

    ////第一步 获取用户token
    public function getAccessToken()
    {
        $data['access_token'] = cache('access_token');
        if ($data['access_token']) {
            return $data;
        }
       if ($this->config['pay_type'] == 1){
            $appid = $this->config["appid"];
            $appSecret = $this->config["appSecret"];
       }else{
           $appid = $this->config["sp_appid"];
           $appSecret = $this->config["m_appSecret"];
       }
        $url_get = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" . $appid . "&secret=" . $appSecret;
        $data = $this->curlGet($url_get);
        $output = json_decode($data, true);
        cache('access_token', $output['access_token'], 7000);
        return $output;
    }
    public function getCode($redirect_uri, $scope = 'snsapi_userinfo', $state = '1')
    {
        if ($this->config['pay_type'] == 1){
            $appid = $this->config["appid"];
            $appSecret = $this->config["appSecret"];
        }else{
            $appid = $this->config["sp_appid"];
            $appSecret = $this->config["m_appSecret"];
        }
        $url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid={$this->config["appid"]}&redirect_uri={$redirect_uri}&response_type=code&scope={$scope}&state={$state}#wechat_redirect";
        header("Location:{$url}");
        exit;
    }
    //网页授权接口调用凭证
    public function getOauth2AccessToken($code)
    {

        if ($this->config['pay_type'] == 1){
            $appid = $this->config["appid"];
            $appSecret = $this->config["appSecret"];
        }else{
            $appid = $this->config["sp_appid"];
            $appSecret = $this->config["m_appSecret"];
        }
        $param = [
            'grant_type' => "authorization_code",
            'code' => $code,//填写第一步获取的 code 参数,
            'secret' => $appSecret,
            'appid' => $appid,
        ];
        $url_get = "https://api.weixin.qq.com/sns/oauth2/access_token?" . http_build_query($param);
        $data = $this->curlGet($url_get);
        $output = json_decode($data, true);


        return $output;
    }
    public function get_user_info($code)
    {
        try {

            $auth2AccessToke = $this->getOauth2AccessToken($code);
            if (isset($auth2AccessToke['errcode'] ) ) {
                return $auth2AccessToke;
            }
            $param = [
                'access_token' => $auth2AccessToke['access_token'],
                'openid' => $auth2AccessToke['openid'],
                'lang' => 'zh_CN',
            ];
            //https://api.weixin.qq.com/sns/userinfo
            $url_get = "https://api.weixin.qq.com/sns/userinfo?" . http_build_query($param);
            $data = $this->curlGet($url_get);
            $userinfo = json_decode($data, true);
            return $userinfo;
        } catch (Exception $e) {
            throw new Exception($e->getMessage());
        }
    }
    //第二步 获取用户信息通过acccess_token和open_id获取用户信息
    public function getUserInfoByToken($access_token, $open_id)
    {
        $param = [
            'access_token' => $access_token,
            'openid' => $open_id,
            'lang' => 'zh_CN',
        ];
        $url = "https://api.weixin.qq.com/cgi-bin/user/info?" . http_build_query($param);
        $data = $this->curlGet($url);
        Log::write($data);
        $userinfo = json_decode($data, true);
        return $userinfo;
    }

    /**
     * Remarks: 自定义菜单
     * User:猫哥
     * Date:2019/11/7
     * Time:14:32
     * Created by PhpStorm.
     */
    public function custom_menu($data)
    {
        //  自定义菜单数据
        $accessTokenData  = $this->getAccessToken(); //  获取到公众号的token值

        $access_token = $accessTokenData['access_token'];
        $url = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token=$access_token";
        $rest = json_post($url, $data);
        return $rest;
    }
    public function wechatpay($array, $openid, $title , $notify_url)
    {
        try {
            $appid = $this->config['appid'];//
            $mchid = $this->config['mchid'];;//商户id
            $xlid = $this->config['serial_number'];//证书序列号
            $time = time(); //时间戳
            $orderid = $array['order_sn'];//订单编号
            $noncestr = md5($orderid . $time . rand());//随机字符串，可以将订单编号存于此处
            $ordertotal = $array['price'] * 100;//
            $url = 'https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi';//生成预支序号所提交路径
            $urlarr = parse_url($url); //路径拆解为：[scheme=>https,host=>api.mch.weixin.qq.com,path=>/v3/pay/transactions/jsapi]
            $data = array();
            $data['appid'] = $appid;
            $data['mchid'] = strval($mchid);
            $data['description'] = $title;//商品描述
            $data['out_trade_no'] = $orderid;//订单编号
            $data['notify_url'] = getProtocol() . $_SERVER["HTTP_HOST"] . '/home.php/' . $notify_url;//回调接口，可以为空
            $data['amount']['total'] = (integer)$ordertotal;//金额 单位 分
            $data['amount']['currency'] = 'CNY';
            $data['payer']['openid'] = $openid;//openid
            $jsonData = json_encode($data); //变为json格式
            $str = "POST" . "\n" . $urlarr['path'] . "\n" . $time . "\n" . $noncestr . "\n" . $jsonData . "\n";
            $signHead = $this->getPrivateSign($str,$this->config['key_file']);
            $token = sprintf('mchid="%s",serial_no="%s",nonce_str="%s",timestamp="%d",signature="%s"', $mchid, $xlid, $noncestr, $time, $signHead);
            $header = array(
                'Content-Type:application/json; charset=UTF-8',
                'Accept:application/json',
                'User-Agent:*/*',
                'Authorization:WECHATPAY2-SHA256-RSA2048 ' . $token
            );
            $res = $this->httpRequest($url, $jsonData, $header);
            $dataArray = json_decode($res, true);
            if (isset($dataArray['prepay_id'])) {
                $prepayID = $dataArray['prepay_id'];
                $prepay = 'prepay_id=' . $prepayID;
                $str = $appid . "\n" . $time . "\n" . $noncestr . "\n" . $prepay . "\n";
                $signPay = $this->getPrivateSign($str,$this->config['key_file']);
                $return = [
                    'paySign' => $signPay,
                    'nonceStr' => $noncestr,
                    'timeStamp' => $time,
                    'package' => $prepay,
                    'appid' => $appid,
                    'signType' => 'RSA'
                ];
            } else {
                return ['code' => 0, 'msg' => $dataArray['message']];
            }

        } catch (\Exception $exception) {
            return ['code' => 0, 'msg' => $exception->getMessage()];
        }
        return ['code' => 1, 'data' => $return];

    }
    function getPrivateSign($content ,$keyFile)
    {
        $binary_signature = "";
        $privateKey = file_get_contents($keyFile);//证书
        $algo = "SHA256";
        //将上传内容与api证书结合生成签名
        openssl_sign($content, $binary_signature, $privateKey, $algo);

        return base64_encode($binary_signature);
    }

    public function mWechatpay($array, $openid,$title, $notify_url)
    {
        try {

            $appid = $this->config['sp_mchid'];//
            $mchid = $this->config['sp_mchid'];;// 服务商户id
            $subMchid = $this->config['sub_mchid'];//
            $xlid = $this->config['m_serial_number'];//证书序列号
            $time = time(); //时间戳
            $orderid = $array['sn_order'];//订单编号
            $noncestr = md5($orderid . $time . rand());//随机字符串，可以将订单编号存于此处
            $ordertotal = $array['reality_amount'] * 100;//
            $url = "https://api.mch.weixin.qq.com/v3/pay/partner/transactions/jsapi";//生成预支序号所提交路径
            $urlarr = parse_url($url);
            $data = array();
            $data['sp_appid'] = $appid;
            $data['sp_mchid'] = strval($mchid);

            $data['sub_mchid'] = strval($subMchid);
            $data['description'] = $title;//商品描述
            $data['out_trade_no'] = $orderid;//订单编号
            $data['notify_url'] = getProtocol() . $_SERVER["HTTP_HOST"] . '/home.php/' . $notify_url;//回调接口，可以为空
            $data['amount']['total'] = (integer)$ordertotal;//金额 单位 分
            $data['amount']['currency'] = 'CNY';
            $data['payer']['sp_openid'] = $openid;//openid
            $jsonData = json_encode($data); //变为json格式
            $str = "POST" . "\n" . $urlarr['path'] . "\n" . $time . "\n" . $noncestr . "\n" . $jsonData . "\n";
            $signHead = $this->getPrivateSign($str,$this->config['m_key_file']);
            $token = sprintf('mchid="%s",nonce_str="%s",timestamp="%d",serial_no="%s",signature="%s"', $mchid, $noncestr, $time, $xlid, $signHead);
            $header = array(
                'Content-Type:application/json; charset=UTF-8',
                'Accept:application/json',
                'User-Agent:*/*',
                'Authorization: WECHATPAY2-SHA256-RSA2048  '. $token
            );
            $res = $this->httpRequest($url, $jsonData, $header);

            $dataArray = json_decode($res, true);
            if (isset($dataArray['prepay_id'])) {
                $prepayID = $dataArray['prepay_id'];
                $prepay = 'prepay_id=' . $prepayID;
                $str = $appid . "\n" . $time . "\n" . $noncestr . "\n" . $prepay . "\n";
                $signPay = $this->getPrivateSign($str,$this->config['m_key_file']);
                $return = [
                    'paySign' => $signPay,
                    'nonceStr' => $noncestr,
                    'timeStamp' => $time,
                    'package' => $prepay,
                    'appid' => $appid,
                    'signType' => 'RSA'
                ];
            } else {
                return ['code' => 0, 'msg' => $dataArray['message']];
            }

        } catch (\Exception $exception) {
            return ['code' => 0, 'msg' => $exception->getMessage()];
        }
        return ['code' => 1, 'data' => $return];


    }
    public function getSign($params, $key1)
    {
        ksort($params); //将参数数组按照参数名ASCII码从小到大排序
        foreach ($params as $key => $item) {
            if (!empty($item)) {  //剔除参数值为空的参数
                $newArr[] = $key . '=' . $item; // 整合新的参数数组
            }
        }
        $stringA = implode("&", $newArr);  //使用 & 符号连接参数
        $stringSignTemp = $stringA . "&key=" . $key1; //拼接key
        // key是在商户平台API安全里自己设置的
        $stringSignTemp = MD5($stringSignTemp); //将字符串进行MD5加密
        $sign = strtoupper($stringSignTemp); //将所有字符转换为大写
        return $sign;
    }

    //加密
    public function getSha256WithRSA($content, $privateKey)
    {
        $binary_signature = "";
        $algo = "SHA256";
        openssl_sign($content, $binary_signature, $privateKey, $algo);
        $sign = base64_encode($binary_signature);
        return $sign;
    }

    public function Message($openid, $data, $template_id)
    {
        /*try {*/
        $accessToken = $this->getAccessToken();
        $url = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=" . $accessToken['access_token'];
        $param = [
            'access_token' => $accessToken['access_token'],
            'touser' => $openid,
            'template_id' => $template_id,
            'data' => $data,

        ];

        $response = $this->json_post($url, $param);
        return $response;
    }
    public  function json_post($url, $data = NULL)
    {
        $curl = curl_init();
        curl_setopt($curl, CURLOPT_URL, $url);
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
        if(!$data){
            return 'data is null';
        }
        if(is_array($data))
        {
            $data = json_encode($data);
        }
        curl_setopt($curl, CURLOPT_POST, 1);
        curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
        curl_setopt($curl, CURLOPT_HEADER, 0);
        curl_setopt($curl, CURLOPT_HTTPHEADER,array(
            'Content-Type: application/json; charset=utf-8',
            'Content-Length:' . strlen($data),
            'Cache-Control: no-cache',
            'Pragma: no-cache'
        ));
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
        $res = curl_exec($curl);
        $errorno = curl_errno($curl);
        if ($errorno) {
            return $errorno;
        }
        curl_close($curl);
        return $res;

    }

    public function getWechatAuthCode()
    {
        // 获取来源地址
        $url = get_url();
        // 获取code
        $urlObj["appid"] = $this->config["appid"];
        //var_dump($_SERVER["REQUEST_URI"])
        $array = explode('?',$_SERVER["REQUEST_URI"]);
        $urlObj["redirect_uri"] = getProtocol() . $_SERVER["HTTP_HOST"] . $array['0']. '?' . urlencode($array[1]);

        $urlObj["response_type"] = "code";
        $urlObj["scope"] = "snsapi_userinfo";
        $urlObj["state"] = "1" . "#wechat_redirect";
        $bizString = $this->formatBizQueryParaMap($urlObj, false);
        $codeUrl = 'https://open.weixin.qq.com/connect/oauth2/authorize?' . $bizString;
        return $codeUrl;
    }

    public function getWxTicket()
    {
        $accessToken = $this->getAccessToken();
        $ticketUrl = 'https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=' . $accessToken['access_token'] . '&type=jsapi';
        $data = $this->curlGet($ticketUrl);
        $output = json_decode($data, true);
        cache('access_token', null);
        if (isset($output['errcode'] ) && $output['errcode'] == '40001') {
            cache('access_token', null);

        }
        return $output;

    }
    public  function  getOrderNumber(){
        return  time().uniqid();
    }

    public function withdrawal($openid, $data)
    {
        $post_data = [
            "appid" => $this->config['appid'],//appid
            "out_batch_no" => $data['sn_order'],//商家批次单号
            "batch_name" => '佣金提现',//批次名称
            "batch_remark" => '佣金提现',//批次备注
            "total_amount" => $data['amount'] * 100,// 转账金额单位为“分”
            "total_num" => 1, // 转账总笔数
            //此处可以多笔提现  组合二维数组放到transfer_detail_list即可   我这里单笔操作，写死了
            "transfer_detail_list" => [
                [
                    'out_detail_no' => $this->getOrderNumber(),
                    'transfer_amount' =>(integer) $data['amount'] * 100,
                    'transfer_remark' => '佣金提现',
                    'openid' => $openid,
                ]
            ]
        ];
        $url = 'https://api.mch.weixin.qq.com/v3/transfer/batches';
        //JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE  防止中文被转义
        $nonce = md5($data['id'] . time() . rand());
        $timestamp = time();
        $url_parts = parse_url($url);
        $canonical_url = ($url_parts['path'] . (!empty($url_parts['query']) ? "?${url_parts['query']}" : ""));
        $param = json_encode($post_data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
        //拼接参数
        $message = 'POST' . "\n" .
            $canonical_url . "\n" .
            $timestamp . "\n" .
            $nonce . "\n" .
            $param . "\n";
        $sslKeyPath = $this->config['key_file'];
        $private_key = openssl_get_privatekey(file_get_contents($sslKeyPath));
        openssl_sign($message, $raw_sign, $private_key, 'sha256WithRSAEncryption');
        $sign = base64_encode($raw_sign);
        $token = sprintf('WECHATPAY2-SHA256-RSA2048 mchid="%s",nonce_str="%s",timestamp="%s",serial_no="%s",signature="%s"', $this->config['mchid'], $nonce, $timestamp, $this->config['serial_number'], $sign);
        $result = $this->wx_post($url, $param, $token);
        $result = json_decode($result, true);
        return $result;
    }

    private function wx_post($url, $param, $token)
    {

        $curl = curl_init();
        $headers = [
            'Authorization: ' . $token,
            'Accept:application/json',
            'Content-Type:application/json;charset=utf-8',
            'User-Agent:Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36',
        ];
        curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($curl, CURLOPT_TIMEOUT, 500);
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
        curl_setopt($curl, CURLOPT_URL, $url);
        curl_setopt($curl, CURLOPT_POSTFIELDS, $param);
        curl_setopt($curl, CURLOPT_POST, true);
        $res = curl_exec($curl);
        curl_close($curl);
        return $res;
    }

    public function httpRequest($url, $data, $header)
    {
        $curl = curl_init(); // 启动一个CURL会话
        curl_setopt($curl, CURLOPT_URL, $url); // 要访问的地址
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0); // 对认证证书来源的检查
        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 0); // 从证书中检查SSL加密算法是否存在，如果出错则修改为0，默认为1
        curl_setopt($curl, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']); // 模拟用户使用的浏览器
        curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1); // 使用自动跳转
        curl_setopt($curl, CURLOPT_AUTOREFERER, 1); // 自动设置Referer
        if (!empty($data)) {
            curl_setopt($curl, CURLOPT_POST, 1); // 发送一个常规的Post请求
            curl_setopt($curl, CURLOPT_POSTFIELDS, $data); // Post提交的数据包
        }
        curl_setopt($curl, CURLOPT_TIMEOUT, 30); // 设置超时限制防止死循环
        curl_setopt($curl, CURLOPT_HEADER, 0); // 显示返回的Header区域内容
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); // 获取的信息以文件流的形式返回
        if (!empty($header)) {
            curl_setopt($curl, CURLOPT_HTTPHEADER, $header);//$header以array格式
        }

        $response = curl_exec($curl); // 执行操作

        if (curl_errno($curl)) {
            echo 'Error:' . curl_error($curl);//捕抓异常
        }

        curl_close($curl); // 关闭CURL会话
        return $response; // 返回数据，json格式
    }

    public function post_url($url, $xml,$keyPem ='',$certPem ='')
    {

        $ch = curl_init();
        curl_setopt($ch, CURLOPT_TIMEOUT, 30);                                    //设置超时
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
        curl_setopt($ch, CURLOPT_HEADER, FALSE);                                //设置header
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);                //要求结果为字符串且输出到屏幕上
        curl_setopt($ch, CURLOPT_POST, TRUE);                                        //POST提交方式
        curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
        $dataxml = curl_exec($ch);
        if ($keyPem){
            curl_setopt($ch,CURLOPT_SSLKEYTYPE,'PEM');
            curl_setopt($ch,CURLOPT_SSLKEY, $keyPem);
        }

        //默认格式为PEM，可以注释
        if ($certPem){
            curl_setopt($ch,CURLOPT_SSLCERTTYPE,'PEM');
            curl_setopt($ch,CURLOPT_SSLCERT,$certPem);
        }

        //运行curl
        if (!$dataxml) {
            $error = curl_errno($ch);
            return ['return_code' => 0, 'msg' => '付款失败,错误码:' . $error];
        }
        curl_close($ch);

        return $this->FromXml($dataxml);
    }

    public function post_url_new($url, $xml,$keyPem,$certPem)
    {

        $ch = curl_init();
        //设置超时
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);//要求结果为字符串且输出到屏幕上
        curl_setopt($ch, CURLOPT_POST, 1);
        //POST提交方式
        curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
        $dataxml = curl_exec($ch);
        if ($keyPem){
            curl_setopt($ch,CURLOPT_SSLKEYTYPE,'PEM');
            curl_setopt($ch,CURLOPT_SSLKEY, $keyPem);
        }
        //默认格式为PEM，可以注释
        if ($certPem){
            curl_setopt($ch,CURLOPT_SSLCERTTYPE,'PEM');
            curl_setopt($ch,CURLOPT_SSLCERT,$certPem);
        }

        //运行curl
        if (!$dataxml) {
            $error = curl_errno($ch);
            return ['return_code' => 0, 'msg' => '付款失败,错误码:' . $error];
        }
        curl_close($ch);

        return $dataxml;
    }

    public function curlGet($url)
    {
        $ch = curl_init();
        $header = array();
        $header[] = "Accept-Charset: utf-8";
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
        curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (compatible; MSIE 5.01; Windows NT 5.0)');
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
        curl_setopt($ch, CURLOPT_AUTOREFERER, 1);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        $result = curl_exec($ch);
        curl_close($ch);
        return $result;
    }

    public function  templateId (){
        $accessToken = $this->getAccessToken();
        $url = 'https://api.weixin.qq.com/cgi-bin/template/get_all_private_template?access_token='.$accessToken['access_token'] ;
        $data = $this->curlGet($url);
        $output = json_decode($data, true);
        if (isset($output['errcode'] ) && $output['errcode'] == '40001') {
            cache('access_token', null);
        }
        return $output;
    }
}